/////////////////////////////////////////////////////////////////////////
// MIT License
//
// Copyright (c) 2000-2011 OpenNETCF Consulting, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 
// documentation files (the "Software"), to deal in the Software without restriction, including without limitation 
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
// and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 
// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  
////////////////////////////////////////////////////////////////////////

using System;
using System.Reflection;
using System.Drawing;
using System.ComponentModel;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Windows.Forms;
using OpenNETCF.Windows.Forms;

namespace OpenNETCF.Windows.Forms.Calendar
{
	/// <summary>
    /// Week calendar control similar to the Windows Mobile calender application
	/// </summary>
	public class WeekCalendar: BaseCalendar
	{
		const int DefaultItemHeight = 20;

        /// <summary>
        /// Occurs when item details are being painted
        /// </summary>
        public DrawItemDetailsDelegate DrawItemDetails;
        /// <summary>
        /// Occurs when the item label is selected
        /// </summary>
        public event EventHandler ItemSelected;
        /// <summary>
        /// Occurs when an item is selected in the calendar
        /// </summary>
        public event EventHandler SelectedItemChanged;

		#region Private data

		private VScrollBar m_sb;
		//protected CalendarItemCollection m_items;
		
		private int m_itemHeight;
		
		private Font m_fntHours;

		private int m_dayBarHeight;
		private int m_hourBarWidth;
		private int m_columnWidth;
		private int m_numDays;
		private int m_firstDay;
		private int m_lastDay;
        private int m_startHour;
        private int m_endHour;

		private Color m_clrTimeOff;
		private Color m_clrTimeOn;
        private Color m_clrDetailsBackground;

        private List<ItemPlaceholder> m_VisibleItems;
        private List<CurrentItem> m_CurrentItems;
        private bool m_needRegenerateLayout;

        private CalendarItem _selectedItem;

        /// <summary>
        /// Gets or sets the selected item in the control
        /// </summary>
        public CalendarItem SelectedItem
        {
            get { return _selectedItem; }
            set 
            {
                if (_selectedItem != value)
                {
                    InvalidateItem(_selectedItem);
                    _selectedItem = value;
                    InvalidateItem(_selectedItem);
                    ShowLabel(_selectedItem != null);
                    if (this.SelectedItemChanged != null)
                        this.SelectedItemChanged(this, EventArgs.Empty);
                }
            }
        }

		#endregion

		/// <summary>
		/// Default contructor
		/// </summary>
		public WeekCalendar()
		{
			m_sb = new VScrollBar();
			Controls.Add(m_sb);
			m_sb.ValueChanged += new EventHandler(m_sb_ValueChanged);
			m_sb.SmallChange = 1;
			m_sb.LargeChange = 1;

			SuspendLayout();

			m_itemHeight = DefaultItemHeight * this.ScaleSize;
			m_hourBarWidth = 25;
			NumDaysInWeek = 5;

            StartHour = 8;
            EndHour = 17;

            ColorTimeOff = Color.FromArgb(222, 223, 222);
            ColorTimeOn = Color.FromArgb(255, 255, 206);
            DetailsBackColor = Color.White;
			Font = new Font("Tahoma", 8, FontStyle.Regular);
			FontLabel = Font;
			FontMonth = new Font("Tahoma", 8, FontStyle.Bold);

			m_weekBarHeight = 16 * this.ScaleSize;
			m_dayBarHeight = m_itemHeight;

			Date = DateTime.Now.Date;

            m_VisibleItems = new List<ItemPlaceholder>();
            m_CurrentItems = new List<CurrentItem>();

            ResumeLayout();
            m_needRegenerateLayout = true;
        }

        private void InvalidateItem(CalendarItem item)
        {
            if (item == null)
                return;
            foreach (ItemPlaceholder ip in m_VisibleItems)
            {
                if (ip.Item.Item == item)
                {
                    Rectangle rc = ip.rcItem;
                    rc.Width = m_columnWidth / ip.Item.Count;
                    rc.Offset(m_columnWidth * ip.Item.Index / ip.Item.Count, -m_sb.Value * m_itemHeight);
                    Invalidate(rc);
                }
            }
        }

		private Rectangle[] CalculateItemRectangles(CurrentItem item)
		{
            //Calculate start date
            DateTime dateStart = Date;
            while (dateStart.DayOfWeek != m_firstDayOfWeek)
                dateStart = dateStart.AddDays(-1);
            dateStart = dateStart.AddDays(m_firstDay);

            int numDays = (int)(item.Item.EndTime.Date.Subtract(item.Item.StartTime.Date).TotalDays + 1);
            List<Rectangle> rcColl = new List<Rectangle>();

            int x = 0, y = 0, cx = 0, cy = 0;
            
            cx = m_columnWidth;
            x = m_hourBarWidth + item.Item.StartTime.Subtract(dateStart).Days * m_columnWidth;
            y = m_rTopBar.Bottom;
            cy = 24 * m_itemHeight;

            for (int i = m_firstDay; i > m_lastDay; i++)
                rcColl.Add(new Rectangle(x + m_columnWidth * i, y, cx, cy));

            // First column
            y = (item.Item.StartTime.Hour) * m_itemHeight + m_rTopBar.Bottom;
            cy = Math.Min( item.Item.Duration.Hours, 24 - item.Item.StartTime.Hour ) * m_itemHeight;
            rcColl.Add(new Rectangle(x, y, cx, cy));

            if (numDays > 1)
            {
                // Last column
                y = m_rTopBar.Bottom;
                cy = item.Item.EndTime.Hour * m_itemHeight;
                rcColl.Add(new Rectangle(x + m_columnWidth * (numDays - 1), y, cx, cy));
            }

            return rcColl.ToArray();
		}

        /// <summary>
        /// Creates offscreen buffer objects
        /// </summary>
        protected override void CreateObjects()
        {
            base.CreateObjects();
        }

        /// <summary>
        /// Raises the DateChanged event
        /// </summary>
        protected override void OnDateChanged()
        {
            base.OnDateChanged();
            DateTime dateStart = Date;
            while (dateStart.DayOfWeek != FirstDayOfWeek)
                dateStart = dateStart.AddDays(-1);
            if (DataProvider != null)
                m_items = DataProvider.GetItemsForDateRange(dateStart, TimeSpan.FromDays(7));
            Recalculate();
        }

        /// <summary>
        /// Recalculates the control
        /// </summary>
		protected override void Recalculate()
		{
            //If the scroll bar is not created yet then this control is not ready and recalculate was called from the base
            if (this.m_sb == null)
                return;


            m_needRegenerateLayout = true;
            if (m_fSuspendLayout)
            {
                m_fRecalcRequired = true;
                return;
            }
            base.Recalculate();

            if (Size == Size.Empty)
                return;

			CreateObjects();

			SizeF sz = m_gxOff.MeasureString("23", m_fntHours);
			m_hourBarWidth = (int)(sz.Width + 2);
			m_columnWidth = (Width - m_sb.Width - m_hourBarWidth) / m_numDays;
			m_hourBarWidth = Width - m_sb.Width - m_columnWidth * m_numDays;
			m_dayBarHeight = m_itemHeight;

			m_leftOffset = Width - m_columnWidth * m_numDays - m_sb.Width;
			m_rTopBar = new Rectangle(0, 0, Width, m_weekBarHeight + m_dayBarHeight);
			m_btnRight.Location = new Point(Width -  m_btnRight.Width - 4, 1);
			m_btnLeft.Location = new Point(m_btnRight.Left - m_btnLeft.Width - 3, m_btnRight.Top);
			
			m_sb.Bounds = new Rectangle(Width - m_sb.Width, m_rTopBar.Height, m_sb.Width, Height - m_rTopBar.Height);
			m_sb.Minimum = 0;
			m_sb.Maximum = (24) - m_sb.Height / m_itemHeight;
			m_sb.SmallChange = 1;

		}

        /// <summary>
        /// Draws the control
        /// </summary>
        /// <param name="g"></param>
        /// <param name="rClip"></param>
		protected override void DrawControl(Graphics g, Rectangle rClip)
		{
			g.Clear(BackColor);
			Point [] Triangle = new Point[3];
			
			//Calculate start date
			DateTime dateStart = Date;
			while( dateStart.DayOfWeek != DayOfWeek.Sunday )
				dateStart = dateStart.AddDays(-1);
			

			// Paint month bar if needed
			if ( m_rTopBar.IntersectsWith(rClip) )
			{
                if (SelectedItem != null)
                {
                    DrawItemDetailsInternal(g, rClip);
                }
                else
                {
                    m_btnMonth.Text = Date.Date.ToString("MMMM");
                    m_btnYear.Text = Date.Date.ToString("yyyy");
                    SizeF szMonth = g.MeasureString(m_btnMonth.Text, FontMonth);
                    SizeF szYear = g.MeasureString(m_btnYear.Text, FontMonth);
                    szYear.Height += 4;
                    szYear.Width += 4;
                    szMonth.Height += 4;
                    szMonth.Width += 4;
                    m_btnMonth.Location = new Point(4, 2);
                    m_btnMonth.Size = new Size((int)szMonth.Width, (int)szMonth.Height);
                    m_btnYear.Location = new Point(m_btnMonth.Right + 3, m_btnMonth.Top);
                    m_btnYear.Size = new Size((int)szYear.Width, (int)szYear.Height);

                    // Draw weekday names
                    for (int i = m_firstDay; i <= m_lastDay; i++)
                    {
                        string sLabel = ((DayOfWeek)i).ToString().Substring(0, 1) + " " + dateStart.AddDays(i).Day.ToString();
                        g.DrawString(sLabel, FontMonth, m_brMonthFG, m_hourBarWidth + (i - m_firstDay) * m_columnWidth + m_columnWidth * .2f - 4, m_rTopBar.Bottom - m_weekBarHeight + 2);
                    }
                    g.DrawRectangle(m_penGrid, m_rTopBar);
                    g.DrawLine(m_penGrid, m_rTopBar.Left, m_rTopBar.Bottom - m_weekBarHeight, m_rTopBar.Width, m_rTopBar.Bottom - m_weekBarHeight);
                    g.DrawLine(m_penGrid, m_btnLeft.Right + 2, m_btnLeft.Top, m_btnLeft.Right + 2, m_btnLeft.Bottom);
                    for (int i = 0; i <= m_numDays; i++)
                        g.DrawLine(m_penGrid, m_hourBarWidth + i * m_columnWidth, m_rTopBar.Bottom - m_weekBarHeight, m_hourBarWidth + i * m_columnWidth, Height);

                }
			}

			Rectangle r = new Rectangle(0, m_rTopBar.Bottom - m_itemHeight, Width, m_itemHeight);

			// Paint each row of cells

			Brush brTimeOn = new SolidBrush(ColorTimeOn);
			Brush brTimeOff = new SolidBrush(ColorTimeOff);

			for( int i = m_sb.Value; i <= m_sb.Value + m_sb.Height / m_itemHeight; i ++ )
			{
				if ( i > 23 )
					break;
				r.Offset(0, m_itemHeight);
				// Ignore rows that do not have to be repainted, not even partially
				if (! r.IntersectsWith(rClip))
					continue;
				// Draw row frame (border)
				Rectangle rCell = new Rectangle(r.X, r.Y + 1, m_hourBarWidth, m_itemHeight);
				g.FillRectangle(m_brDayBG, rCell);
				g.DrawLine(m_penGrid, m_hourBarWidth, r.Top, m_hourBarWidth, r.Bottom);
				int nDay = ((i + 11) % 12) + 1;
				SizeF szHour = g.MeasureString(nDay.ToString(), m_fntHours);
				g.DrawString(nDay.ToString(), m_fntHours, m_brMonthFG, (float)(rCell.Right) - szHour.Width - 1, r.Y + 1);

				rCell = new Rectangle(m_hourBarWidth, r.Y, m_numDays * m_columnWidth, m_itemHeight);
				//
				for( int j = 0; j <= m_numDays; j++ )
				{
					if ( rCell.IntersectsWith(rClip) )
					{
						RectangleF rCellF = new RectangleF(rCell.X, rCell.Y, m_columnWidth, m_itemHeight);
						DateTime dtThis = dateStart.AddDays(i * 7 + j);
						g.FillRectangle( (i < m_startHour || i > m_endHour)? brTimeOff: brTimeOn, rCell);
                        g.DrawLine(m_penGrid, rCell.Left, rCell.Top, rCell.Left, rCell.Bottom);
					}
					rCell.Offset(m_columnWidth, 0);
				}
			}

            if (m_needRegenerateLayout)
            {
                m_needRegenerateLayout = false;
                m_VisibleItems.Clear();
                m_CurrentItems.Clear();
                LayoutItems();
            }

            foreach (ItemPlaceholder ip in m_VisibleItems)
            {
                Rectangle rc = ip.rcItem;
                rc.Width = m_columnWidth / ip.Item.Count;
                rc.Offset(m_columnWidth * ip.Item.Index / ip.Item.Count, -m_sb.Value * m_itemHeight);
                if (rc.Y < m_rTopBar.Bottom )
                {
                    if (rc.Bottom < m_rTopBar.Bottom)
                        continue;
                    rc.Height -= m_rTopBar.Bottom - rc.Y;
                    rc.Y = m_rTopBar.Bottom;
                }
                rc.Inflate(-2, -2);
                g.FillRectangle(m_brLabelBG, rc);
                g.DrawRectangle(m_penGrid, rc);
                rc.Inflate(1, 1);
                g.DrawRectangle(m_penFrame, rc);
                if (ip.Item.Item == SelectedItem)
                {
                    Pen penSelected = new Pen(SystemColors.Window);
                    rc.Inflate(-1, -1);
                    g.DrawRectangle(penSelected, rc);
                    penSelected.Dispose();
                }
            }

			brTimeOff.Dispose();
			brTimeOn.Dispose();

			g.DrawLine(m_penGrid, m_rTopBar.Left, m_rTopBar.Bottom, m_rTopBar.Width, m_rTopBar.Bottom);
			g.DrawRectangle(m_penGrid, 0, 0, Width - 1, Height - 1);

#if EVAL_VERSION
            UIUtils.PaintWatermark(g, this.Width, this.Height);
#endif
        }

        private void ShowLabel(bool bShow)
        {
            if (bShow)
            {
                m_btnLeft.Hide();
                m_btnRight.Hide();
                this.m_btnToday.Hide();
                m_btnMonth.Hide();
                m_btnYear.Hide();
            }
            else
            {
                this.m_btnToday.Show();
                m_btnLeft.Show();
                m_btnRight.Show();
                m_btnMonth.Show();
                m_btnYear.Show();
            }
            Rectangle rcLabel = new Rectangle(0, 0, Width, m_rTopBar.Height + m_itemHeight);
            Invalidate(rcLabel);
        }

        private void DrawItemDetailsInternal(Graphics g, Rectangle rcClip)
        {
            if ( SelectedItem == null )
                return;

            Rectangle rcLabel = m_rTopBar; //new Rectangle(0, 0, Width, m_rTopBar.Height + m_itemHeight);
            if (!rcClip.IntersectsWith(rcLabel))
                return;

            SolidBrush brBG = new SolidBrush(DetailsBackColor);
            g.FillRectangle(brBG, rcLabel);
            brBG.Dispose();
            
            if (DrawItemDetails != null)
            {
                DrawItemDetails(g, rcClip, SelectedItem);
                return;
            }

            string sLabel;
            if ( SelectedItem.StartTime.Date == SelectedItem.EndTime.Date)
                sLabel = string.Format("{2}\n{0} - {1}", SelectedItem.StartTime.ToString("hh:mm tt"), SelectedItem.EndTime.ToString("hh:mm tt"), SelectedItem.Text);
            else
                sLabel = string.Format("{2}\n{0} - {1}", SelectedItem.StartTime.ToString("MM/dd/yy hh:mm tt"), SelectedItem.EndTime.ToString("MM/dd/yy hh:mm tt"), SelectedItem.Text);
            SolidBrush brLabel = new SolidBrush(ForeColor);
            g.DrawString(sLabel, m_fntLabel, brLabel, 2, 1);
            brLabel.Dispose();
            rcLabel.Width -= 1;
            rcLabel.Height -= 1;
            g.DrawRectangle(m_penGrid, rcLabel);
        }

        /// <summary>
        /// Raises the OpenNETCF.Windows.Forms.Calendar.WeekCalendar.ItemSelected event
        /// </summary>
        protected void OnItemSelected()
        {
            if (ItemSelected != null)
                ItemSelected(this, new EventArgs());
        }

        /// <summary>
        /// Raises the System.Windows.Forms.Control.Resize event
        /// </summary>
        /// <param name="e"></param>
		protected override void OnResize(EventArgs e)
		{
			base.OnResize (e);
			Recalculate();
            Invalidate();
        }

        /// <summary>
        /// Raises the SYstem.Windows.Forms.Control.FontChanged event
        /// </summary>
		protected override void OnFontChanged()
		{
			base.OnFontChanged ();
			m_fntHours = new Font(Font.Name, Font.Size, FontStyle.Bold);
			Recalculate();
            Invalidate();
		}

        /// <summary>
        /// Raises the OpenNETCF.Windows.Forms.Calendar.WeekCalendar.MonthFontChanged event
        /// </summary>
        protected override void OnMonthFontChanged()
        {
            base.OnMonthFontChanged();
            Recalculate();
            Invalidate();
        }

        /// <summary>
        /// Raises the OpenNETCF.Windows.Forms.Calendar.WeekCalendar.LabelFontChanged event
        /// </summary>
        protected override void OnLabelFontChanged()
        {
            m_weekBarHeight = (int)(FontLabel.Size * 2) * this.ScaleSize;
            Recalculate();
            Invalidate();
        }

        /// <summary>
        /// Raises the System.Windows.Forms.Control.MouseDown event
        /// </summary>
        /// <param name="e"></param>
        protected override void OnMouseDown(MouseEventArgs e)
        {
            HitTestInfo hti = HitTest(e.X, e.Y);
            if (hti.Type == HitTestType.Cell)
            {
                CalendarItem item = null;
                foreach (ItemPlaceholder ip in m_VisibleItems)
                {
                    Rectangle rc = ip.rcItem;
                    rc.Width = m_columnWidth / ip.Item.Count;
                    rc.Offset(m_columnWidth * ip.Item.Index / ip.Item.Count, -m_sb.Value * m_itemHeight);
                    if (rc.Contains(hti.Location))
                    {
                        item = ip.Item.Item;
                        break;
                    }
                }
                SelectedItem = item;
                //Capture = true;
            }
            else if (SelectedItem != null && hti.Type == HitTestType.ItemDetails)
            {
                OnItemSelected();
            }
            base.OnMouseDown(e);
        }

        /// <summary>
        /// Determines the location of the x,y co-ordinates on the week calendar control
        /// </summary>
        /// <param name="x">x value of the click</param>
        /// <param name="y">y value of the click</param>
        /// <returns><see cref="HitTestInfo">HitTestInfo</see> structure.</returns>
        public HitTestInfo HitTest(int x, int y)
        {
            HitTestInfo hti = new HitTestInfo();
            hti.Location = new Point(x, y);
            hti.Type = HitTestType.Nowhere;
            if (m_rTopBar.Contains(x, y))
            {
                hti.Type = HitTestType.ItemDetails;
                return hti;
            }
            if (x <= m_hourBarWidth)
            {
                hti.Type = HitTestType.Hours;
                return hti;
            }

            hti.Type = HitTestType.Cell;
            return hti;
        }

        /// <summary>
        /// Gets or sets the start hour for the business days
        /// </summary>
        public int StartHour
        {
            get { return m_startHour; }
            set { m_startHour = value; Invalidate(); }
        }

        /// <summary>
        /// Gets or sets the end hour for the business days
        /// </summary>
        public int EndHour
        {
            get { return m_endHour; }
            set { m_endHour = value; Invalidate(); }
        }

		/// <summary>
		/// Gets or sets the number of days in a week
		/// </summary>
		public int NumDaysInWeek
		{
			get { return m_numDays; }
			set
			{
				if ( value < 5 || value > 7 )
					throw new ArgumentException("NumDaysInWeek must be between 5 and 7");
				m_numDays = value;
				switch(value)
				{
					case 5:
						m_firstDay = 1;
						m_lastDay = 5;
						break;
					case 6:
						m_firstDay = 1;
						m_lastDay = 6;
						break;
					case 7:
						m_firstDay = 0;
						m_lastDay = 6;
						break;
				}

				Recalculate();
				Invalidate();
			}
		}

        /// <summary>
        /// Gets or sets the color of the area depicting free time
        /// </summary>
		public Color ColorTimeOff
		{
			get { return m_clrTimeOff; }
			set { m_clrTimeOff = value; Invalidate(); }
		}

        /// <summary>
        /// Gets or sets the color of the area depicting busy time
        /// </summary>
		public Color ColorTimeOn
		{
			get { return m_clrTimeOn; }
			set { m_clrTimeOn = value; Invalidate(); }
		}

        /// <summary>
        /// Gets or sets the color of the area depicting details
        /// </summary>
		public Color DetailsBackColor
		{
            get { return m_clrDetailsBackground; }
            set { m_clrDetailsBackground = value; Invalidate(m_rTopBar); }
		}

        

		#region IDisposable Members

        /// <summary>
        /// Releases the unmanaged resources of OpenNETCF.Windows.Forms.Calendar.WeekCalendar control
        /// </summary>
		public new void Dispose()
		{
			base.Dispose(true);
			if ( m_gxOff != null )
				m_gxOff.Dispose();
			if ( m_bmOff != null )
				m_bmOff.Dispose();
		}

		#endregion

      
        /// <summary>
        /// Defines the type of hit test that resulted from an X,Y co-ordinates
        /// </summary>
		public enum HitTestType
		{
            /// <summary>
            /// Item details section was hit
            /// </summary>
			ItemDetails,
            /// <summary>
            /// Cell within the control was hit
            /// </summary>
			Cell,
            /// <summary>
            /// Nothing was hit
            /// </summary>
            Nowhere,
            /// <summary>
            /// Hours item was hit
            /// </summary>
            Hours,
            /// <summary>
            /// The grid was hit
            /// </summary>
            Grid
		}

        /// <summary>
        /// Describes hit test results for the calendar control
        /// </summary>
        public struct HitTestInfo
        {
            /// <summary>
            /// The type of hit test
            /// </summary>
            public HitTestType Type;
            /// <summary>
            /// The location of the hit test
            /// </summary>
            public Point Location;
            /// <summary>
            /// The date associated with the hittest
            /// </summary>
            public DateTime Date;
        }

        
        private void LayoutItems()
        {
            List<CalendarItem> slots = new List<CalendarItem>();

            DateTime day1 = m_date.Date;
            while (day1.DayOfWeek > m_firstDayOfWeek)
                day1 = day1.Subtract(TimeSpan.FromDays(1));
            day1 = day1.AddDays(m_firstDay);

            foreach (CalendarItem item in m_items)
                if (item.EndTime > day1 && item.StartTime < day1.AddDays(m_numDays))
                    m_CurrentItems.Add(new CurrentItem(item));

            int max = 0, last = 0, count = 0;
            //slots.Add(new List<CalendarItem>());
            for (int idx = 0; idx < m_CurrentItems.Count; idx++)
            {
                CalendarItem item = m_CurrentItems[idx].Item;
                for (int i = 0; i < slots.Count; i++)
                    if (slots[i].EndTime <= item.StartTime)
                        slots[i] = null;

                for (int i = 0; i < slots.Count && count > 0; ++i)
                {
                    if (slots[i] != null && slots[i].EndTime <= item.StartTime)
                    {
                        slots[i] = null;
                        if (--count == 0)
                        {
                            int l = last;
                            for (; l < idx; ++l)
                                m_CurrentItems[l].Count = max;
                            last = l;
                            max = 0;
                        }
                    }
                }


                int index = slots.IndexOf(null);
                if (index == -1)
                {
                    slots.Add(item);
                    index = slots.Count - 1;
                }

                //
                // Assign a free slot
                // 
                slots[index] = item;
                m_CurrentItems[idx].Index = index;
                if (index >= max) max = index + 1;
                ++count;


            }

            for (int l = last; l < m_CurrentItems.Count; ++l)
                m_CurrentItems[l].Count = max;

            int maxCount = slots.Count;

            m_VisibleItems.Clear();
            for (int i = 0; i < m_CurrentItems.Count; i++)
            {
                //rc.Width = m_columnWidth / m_CurrentItems[i].Count;
                //rc.Offset(m_columnWidth * m_CurrentItems[i].Index / m_CurrentItems[i].Count, 0);
                foreach (Rectangle rc in CalculateItemRectangles(m_CurrentItems[i]))
                {
                    ItemPlaceholder placeHolder = new ItemPlaceholder(m_CurrentItems[i]);
                    placeHolder.rcItem = rc;
                    m_VisibleItems.Add(placeHolder);
                }
            }
        }

		private void m_sb_ValueChanged(object sender, EventArgs e)
		{
			Rectangle r = new Rectangle(0, m_rTopBar.Bottom, Width - m_sb.Width, Height - m_rTopBar.Bottom);
			Invalidate(r);
		}

        /// <summary>
        /// 
        /// </summary>
        protected override void OnItemListChanged()
        {
            m_needRegenerateLayout = true;
            SelectedItem = null;
            base.OnItemListChanged();
        }
	}

    /// <summary>
    ///  Represents the method that will handle the event when an item details is being drawn
    /// </summary>
    /// <param name="g"></param>
    /// <param name="rcClip"></param>
    /// <param name="item"></param>
    public delegate void DrawItemDetailsDelegate(Graphics g, Rectangle rcClip, CalendarItem item);
}
